﻿using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Windows.Forms;
using System.ComponentModel;


namespace AAA.Base.Util.Hook
{
    public class KeyboardHook
    {
        const int WH_KEYBOARD_LL = 13;

        public delegate int HookProc(int nCode, IntPtr wParam, IntPtr lParam);
        public delegate void HookEventHandler(object sender, HookEventArgs e);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);

        // 設置掛鉤.
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

        // 將之前設置的掛鉤移除。記得在應用程式結束前呼叫此函式.
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);

        // 呼叫下一個掛鉤處理常式（若不這麼做，會令其他掛鉤處理常式失效）.
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode, IntPtr wParam, IntPtr lParam);

        [DllImport("kernel32.dll")]
        public static extern int GetCurrentThreadId();

        [DllImport("user32.dll")]
        public static extern short GetKeyState(VirtualKeys nVirtKey);

        [DllImport("user32.dll")]
        public static extern short GetAsyncKeyState(VirtualKeys nVirtKey); 

        public event HookEventHandler HookInvoked;

        private static int _iHookHandle = 0;    // Hook handle
        private HookProc _KbdHookProc;            // 鍵盤掛鉤函式指標

        private string _strErrorMessage;

        public string ErrorMessage
        {
            get { return _strErrorMessage; }
            set { _strErrorMessage = value; }
        }

        public KeyboardHook()
        {            
        }

        public bool StartHook()
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {                
                _KbdHookProc = new HookProc(KeyboardHookProc);
                _iHookHandle = SetWindowsHookEx(WH_KEYBOARD_LL, _KbdHookProc, GetModuleHandle(curModule.ModuleName), 0);
            }

            if (_iHookHandle == 0)
            {                
                Console.WriteLine("呼叫 SetWindowsHookEx 失敗!");
                return false;
            }
            return true;
        }

        public bool StopHook()
        {
            try
            {
                bool ret = UnhookWindowsHookEx(_iHookHandle);


                if (ret == false)
                {
                    ErrorMessage = "UnhookWindowsHookEx Fail";
                    return false;
                }
                _iHookHandle = 0;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message + "," + ex.StackTrace);
            }
            return true;
        }

        public int KeyboardHookProc(int nCode, IntPtr wParam, IntPtr lParam)
        {
            // 當按鍵按下及鬆開時都會觸發此函式，這裡只處理鍵盤按下的情形。
            bool isPressed = (lParam.ToInt32() & 0x80000000) == 0;

            if (nCode < 0 || !isPressed)
            {
                return CallNextHookEx(_iHookHandle, nCode, wParam, lParam);
            }

            if (HookInvoked != null)
            {
                Keys key = Keys.NoName;
                foreach(Keys currentKey in Enum.GetValues(typeof(Keys)))
                {
                    if((currentKey == Keys.Menu) ||
                       (currentKey == Keys.Shift) ||
                       (currentKey == Keys.Control) ||
                       (currentKey == Keys.ShiftKey) ||
                       (currentKey == Keys.ControlKey))
                        continue;

                    if(KeyboardInfo.GetKeyState(currentKey).IsPressed)
                    {
                        key = currentKey;
                        break;
                    }
                }
                 
                HookEventArgs eventArgs = new HookEventArgs();
                eventArgs.key = key;
                eventArgs.lParam = lParam;
                eventArgs.wParam = wParam;
                eventArgs.HookCode = nCode;
                eventArgs.isPressedAltKey = KeyboardInfo.GetKeyState(Keys.Menu).IsPressed;
                eventArgs.isPressedCtrlKey = KeyboardInfo.GetKeyState(Keys.Control).IsPressed || KeyboardInfo.GetKeyState(Keys.ControlKey).IsPressed;
                eventArgs.isPressedShiftKey = KeyboardInfo.GetKeyState(Keys.Shift).IsPressed || KeyboardInfo.GetKeyState(Keys.ShiftKey).IsPressed;
                HookInvoked(this, eventArgs);
            }

            return CallNextHookEx(_iHookHandle, nCode, wParam, lParam);
        }

    }

        public class KeyboardInfo
        {
            private KeyboardInfo() { }

            [DllImport("user32")]
            private static extern short GetAsyncKeyState(int vKey);

            public static KeyStateInfo GetKeyState(Keys key)
            {
                int vkey = (int)key;

                if (key == Keys.Alt)
                {
                    vkey = 0x12;    // VK_ALT
                }

                short keyState = GetAsyncKeyState(vkey);
                int low = Low(keyState);
                int high = High(keyState);
                bool toggled = (low == 1);
                bool pressed = (high == 1);

                return new KeyStateInfo(key, pressed, toggled);
            }

            public static int High(int keyState)
            {
                if (keyState > 0)
                {
                    return keyState >> 0x10;
                }
                else
                {
                    return (keyState >> 0x10) & 0x1;
                }
            }

            public static int Low(int keyState)
            {
                return keyState & 0xffff;
            }
        }

        public struct KeyStateInfo
        {
            Keys _Key;
            bool _isPressed;
            bool _isToggled;

            public KeyStateInfo(Keys key, bool ispressed, bool istoggled)
            {
                _Key = key;
                _isPressed = ispressed;
                _isToggled = istoggled;
            }

            public static KeyStateInfo Default
            {
                get
                {
                    return new KeyStateInfo(Keys.None, false, false);
                }
            }

            public Keys Key
            {
                get { return _Key; }
            }
        
            public bool IsPressed
            {
                get { return _isPressed; }
            }

            public bool IsToggled
            {
                get { return _isToggled; }
            }
        }


/*
        /// <summary>  
        /// 提供 GlobalKeyDown 或 GlobalKeyUp 事件的資料。  
        /// </summary>  
        public class KeyEventArgs : EventArgs  
        {  
            /// <summary>  
            /// 取得或設定值，指出是否處理事件。  
            /// </summary>  
            public bool Handled { get; set; }  
            /// <summary>  
            /// 取得值，虛擬鍵盤碼的System.Windows.Forms.Keys表示。  
            /// </summary>  
            public System.Windows.Forms.Keys Keys { get { return (System.Windows.Forms.Keys)VirtualKeyCode; } }  
            /// <summary>  
            /// 取得值，虛擬鍵盤碼的System.Windows.Input.Key表示。  
            /// </summary>  

    //        public System.Windows.Input.Key Key { get { return System.Windows.Input.KeyInterop.KeyFromVirtualKey(VirtualKeyCode); } }  
            /// <summary>  
            /// 取得值，指出是否按下 ALT 鍵。  
            /// </summary>  
        
            public bool Alt  
            {  
                get 
                {  
                    return KeyIsDown((int)System.Windows.Forms.Keys.LMenu) || KeyIsDown((int)System.Windows.Forms.Keys.RMenu);  
                }  
            }  

            /// <summary>  
            /// 取得值，指出是否按下 CTRL 鍵。  
            /// </summary>  

            public bool Control  
            {  
                get 
                {  
                    return KeyIsDown((int)System.Windows.Forms.Keys.LControlKey) || KeyIsDown((int)System.Windows.Forms.Keys.RControlKey);  
                }  
            }  
        
            /// <summary>  
            /// 取得值，指出是否按下 SHIFT 鍵。  
            /// </summary>  

            public bool Shift  
            {  
                get 
                {  
                    return KeyIsDown((int)System.Windows.Forms.Keys.LShiftKey) || KeyIsDown((int)System.Windows.Forms.Keys.RShiftKey);  
                }  
            }  

            /// <summary>  
            /// 取得值，引發事件的虛擬鍵盤碼。  
            /// </summary>  

            public int VirtualKeyCode { get; private set; }  

            internal KeyEventArgs(int virtualKey)  
            {  
                this.Handled = false;  
                this.VirtualKeyCode = virtualKey;  
            }  

            private static bool KeyIsDown(int KeyCode)  
            {
                return false;

            }  
        }  
     * */
}
